/**
 * Copyright (c) 2018-2028.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sailmi.auth.utils;

import com.sailmi.common.constant.TokenConstant;
import com.sailmi.common.exception.SecureException;
import com.sailmi.common.secure.client.IClientDetails;
import com.sailmi.common.secure.client.IClientDetailsService;
import com.sailmi.common.tool.utils.Func;
import com.sailmi.common.model.auth.AuthInfo;
import com.sailmi.common.model.auth.AuthUser;
import com.sailmi.common.model.auth.TokenInfo;
import com.sailmi.common.tool.utils.SecureUtil;
import com.sailmi.common.tool.utils.SpringUtil;
import com.sailmi.common.tool.utils.StringUtil;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static com.sailmi.common.constant.TokenConstant.CLIENT_ID;
import static com.sailmi.common.tool.utils.SecureUtil.*;

/**
 * 认证工具类
 *
 * @author yzh
 */
public class TokenUtil {

	public final static String CAPTCHA_HEADER_KEY = "Captcha-Key";
	public final static String CAPTCHA_HEADER_CODE = "Captcha-Code";
	public final static String CAPTCHA_NOT_CORRECT = "验证码不正确";
	public final static String TENANT_HEADER_KEY = "Tenant-Id";
	public final static String DEFAULT_TENANT_ID = "000000";
	public final static String USER_TYPE_HEADER_KEY = "User-Type";
	public final static String DEFAULT_USER_TYPE = "web";
	public final static String USER_NOT_FOUND = "用户名或密码错误";
	public final static String HEADER_KEY = "Authorization";
	public final static String HEADER_PREFIX = "Basic ";
	public final static String DEFAULT_AVATAR = "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png";
	private final static Logger log = LoggerFactory.getLogger(TokenUtil.class);

	static IClientDetailsService clientDetailsService;

	static {
		clientDetailsService = SpringUtil.getBean(IClientDetailsService.class);
	}
	/**
	 * 创建认证token
	 *
	 * @param user 用户信息
	 * @return token
	 */
	public static AuthInfo createAuthInfo(AuthUser user) {
		//设置jwt参数
		Map<String, String> param = new HashMap<>(16);
		param.put(TokenConstant.TOKEN_TYPE, TokenConstant.ACCESS_TOKEN);
		param.put(TokenConstant.TENANT_ID, user.getTenantId());
		param.put(TokenConstant.USER_ID, Func.toStr(user.getUserId()));
		param.put(TokenConstant.ROLE_ID, user.getRoleIdString());
		param.put(TokenConstant.ACCOUNT, user.getAccount());
		param.put(TokenConstant.USER_NAME, user.getUserName());
		param.put(TokenConstant.USER_CODE,user.getUserCode());
		param.put(TokenConstant.ROLE_NAME, Func.join(user.getRoles()));
		param.put(TokenConstant.ENTERPRISE_ID, Func.toStr(user.getEnterprise()));

		//------------check client start
		String[] tokens = SecureUtil.extractAndDecodeHeader();
		assert tokens.length == 2;
		String clientId = tokens[0];
		String clientSecret = tokens[1];
		System.out.println("SecureUtil:createAuthInfo:clientId:"+clientId);
		System.out.println("SecureUtil:createAuthInfo:clientSecret:"+clientSecret);
		//-----check client end!
		System.out.println("...param:"+param);
		System.out.println("...token constant:"+TokenConstant.ACCESS_TOKEN);

		TokenInfo accessToken = createJWT(param, "audience", "issuser", TokenConstant.ACCESS_TOKEN);

		AuthInfo authInfo = new AuthInfo();
		authInfo.setUserId(user.getUserId().toString());
		authInfo.setAuthUser(user);
		authInfo.setAccount(user.getAccount());
		authInfo.setUserName(user.getUserName());
		authInfo.setAuthority(Func.join(user.getRoles()));
		authInfo.setAccessToken(accessToken.getToken());
		authInfo.setUserCode(user.getUserCode());
		authInfo.setExpiresIn(user.getExpiresIn());
		authInfo.setRefreshToken(createRefreshToken(user).getToken());
		authInfo.setTokenType(TokenConstant.BEARER);
		authInfo.setLicense(TokenConstant.LICENSE_NAME);
		return authInfo;
	}

	/**
	 * 创建refreshToken
	 *
	 * @param user 用户信息
	 * @return refreshToken
	 */
	private static TokenInfo createRefreshToken(AuthUser user) {
		Map<String, String> param = new HashMap<>(16);
		param.put(TokenConstant.TOKEN_TYPE, TokenConstant.REFRESH_TOKEN);
		param.put(TokenConstant.USER_ID, Func.toStr(user.getUserId()));

		//------------check client start
		String[] tokens = SecureUtil.extractAndDecodeHeader();

		assert tokens.length == 2;
		String clientId = tokens[0];
		String clientSecret = tokens[1];
		log.info("SecureUtil:createRefreshToken:clientId:"+clientId);
		log.info("SecureUtil:createRefreshToken:clientSecret:"+clientSecret);
		//-----check client end!

		return createJWT(param, "audience", "issuser", TokenConstant.REFRESH_TOKEN);
	}
	/**
	 * 创建令牌
	 *
	 * @param user      user
	 * @param audience  audience
	 * @param issuer    issuer
	 * @param tokenType tokenType
	 * @return jwt
	 */
	public static TokenInfo createJWT(Map<String, String> user, String audience, String issuer, String tokenType) {

		String[] tokens = SecureUtil.extractAndDecodeHeader();
		assert tokens.length == 2;
		System.out.println("SecureUtil:Tokens:"+tokens);
		String clientId = tokens[0];
		String clientSecret = tokens[1];

		// 获取客户端信息
		IClientDetails clientDetails = clientDetails(clientId);
		// 校验客户端信息
		if (!validateClient(clientDetails, clientId, clientSecret)) {

			//System.out.println("SecureUtil:clientDetails:"+clientDetails);
			//System.out.println("SecureUtil:clientId:"+clientId);
			//System.out.println("SecureUtil:clientSecret:"+clientSecret);
			throw new SecureException("客户端认证失败!");
		}

		SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

		long nowMillis = System.currentTimeMillis();
		Date now = new Date(nowMillis);

		//生成签名密钥
		byte[] apiKeySecretBytes = Base64.getDecoder().decode(BASE64_SECURITY);
		Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

		//添加构成JWT的类
		JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JsonWebToken")
			.setIssuer(issuer)
			.setAudience(audience)
			.signWith(signatureAlgorithm, signingKey)
			.setIssuedAt(now);

		//设置JWT参数
		user.forEach(builder::claim);

		//设置应用id
		builder.claim(CLIENT_ID, clientId);

		//添加Token过期时间
		long expireMillis;
		if (tokenType.equals(TokenConstant.ACCESS_TOKEN)) {
			expireMillis = clientDetails.getAccessTokenValidity() * 1000;
		} else if (tokenType.equals(TokenConstant.REFRESH_TOKEN)) {
			expireMillis = clientDetails.getRefreshTokenValidity() * 1000;
		} else {
			expireMillis = getExpire();
		}
		long expMillis = nowMillis + expireMillis;
		Date exp = new Date(expMillis);
		builder.setExpiration(exp).setNotBefore(now);

		// 组装Token信息
		TokenInfo tokenInfo = new TokenInfo();
		tokenInfo.setToken(builder.compact());
		tokenInfo.setExpire((int) expireMillis / 1000);

		return tokenInfo;
	}


	/**
	 * 获取客户端信息
	 *
	 * @param clientId 客户端id
	 * @return clientDetails
	 */
	private static IClientDetails clientDetails(String clientId) {
		return clientDetailsService.loadClientByClientId(clientId);
	}

	/**
	 * 校验Client
	 *
	 * @param clientId     客户端id
	 * @param clientSecret 客户端密钥
	 * @return boolean
	 */
	private static boolean validateClient(IClientDetails clientDetails, String clientId, String clientSecret) {
		if (clientDetails != null) {
			return StringUtil.equals(clientId, clientDetails.getClientId()) && StringUtil.equals(clientSecret, clientDetails.getClientSecret());
		}
		return false;
	}

}
